import requests as rq
from html.parser import HTMLParser


FILE_SECIDS = 'Cache/secids.dat'  # secids.dat 文件路径
SECIDS = ['0.', '1.']  # 可能的secid
FILE_TEMPPIC = 'Cache/tempStockTrend.png'
FILE_TRYALLERROR = 'ErrorLog/TryAll.log'
FILE_RESPONSEERROR = 'ErrorLog/Response.log'


def tryallsecidlog(text):
    """
    尝试所有secid都未成功的错误日志

    :return:
    """
    with open(FILE_TRYALLERROR, 'a') as file:
        file.write(text)


def responselog(url, params, response):
    """
    记录返回数据错误

    :param url: 地址
    :param params: 请求参数
    :param response: 返回值
    :return:
    """
    log = 'url: %s\nParams: %s\nResponse: %s\n\n' % (url, params, response)
    with open(FILE_RESPONSEERROR, 'a') as file:
        file.write(log)


def read_secids():
    """
    获取本地存储secids

    :return:
        【dict】 - {'300750': '0.'}
    """
    import os

    secids_dict = {}  # 创建新的空的secids字典
    # 打开secid缓存文件
    if not os.path.isfile(FILE_SECIDS):
        print('secids.dat文件不存在！！！')
        pass
    else:
        # 文件存在，直接读文件，并转换为字典
        with open(FILE_SECIDS, 'r') as file:
            file_content = file.read()
            if not file_content:
                # 文件内容为空
                print('secids.dat内容为空！！！')
                pass
            else:
                # 尝试转换为字典，如果报错内容被损坏
                try:
                    secids_dict = eval(file_content)
                except SyntaxError:
                    print('secids.dat 内容损坏！！！')
                    pass
    # 校验secid是否符合规则
    for key in secids_dict.keys():
        if not secids_dict[key] in SECIDS:
            print(f'{key} secid不符合规则！！！')
            # 给个默认secid
            secids_dict[key] = '0.'

    return secids_dict


def write_secids(secids: dict):
    """
    将成功的secids存储到缓存中

    :param secids: 成功的secids字典
    :return:
    """
    # 存储成功的secids
    with open(FILE_SECIDS, 'w') as file:
        file.write(str(secids))


def requests_get(url, headers=None, params=None, timeout=30, often=False):
    """
    requests get请求

    :param url: 访问地址
    :param headers: 请求头
    :param params: 请求参数
    :param timeout: 请求超时时间 单位 s
    :param often: 是否开启频繁访问，如果为True不会延时直接访问，
    :return:
        <rq对象>
        【'Time out'】 请求超时
        【'Network error'】 网络连接错误
    """
    from time import sleep
    from random import randint

    try:
        # 间隔访问，防止频繁访问被拉黑
        if not often:
            sleep(randint(200, 1000) / 1000)
        return rq.get(url, headers=headers, params=params, timeout=timeout)
    except rq.exceptions.ConnectTimeout:  # 请求超时
        return 'Time out'
    except rq.exceptions.ConnectionError:  # 网络连接错误
        return 'Network error'


def connect_dfcf(often=False):
    """
    https://www.eastmoney.com/  # 东方财富官网
    检查连接是否正常，获取时间

    :param often: 是否开启无延时访问
    :return:
        【'Time out'】 请求超时
        【'Network error'】 网络连接错误
        【int】 - 1626245924 时间戳，秒级
    """
    import time

    # 访问东方财富网
    response = requests_get('https://www.eastmoney.com/', timeout=8, often=often)
    if isinstance(response, str):
        # 访问出错！！！
        return response

    date = response.headers['date']
    # print(f'响应头时间：{date}')
    ltime = time.strptime(date[5:25], "%d %b %Y %H:%M:%S")
    time_stamp = time.mktime(ltime) + 8 * 60 * 60  # 将GMT时间转换成北京时间
    # print(time_stamp)

    return int(time_stamp)


def try_get(code, url, sectype='secid', headers=None, params=None):
    """
    尝试secid

    :param code: 股票代码
    :param sectype: 参数形式，因为获股票价格和个股信息secid参数形式不一样
    :param url: 访问地址
    :param headers: 请求头
    :param params: 请求参数
    :return:
        【'Time out'】 请求超时
        【'Network error'】 网络连接错误
        【list】 - 尝试成功。[0] - 成功的secid，[1] - 成功的数据
    """
    for secid_ in SECIDS:
        secid = secid_ + str(code)
        print(f'可能的secid: {secid}')
        # 尝试访问
        params[sectype] = secid
        response = requests_get(url, headers, params)
        if isinstance(response, str):
            # 返回如果是str,说明访问出错
            return response
        # 判断访问是否有数据
        jsondata = response.json()
        print(f'返回结果(json)：{jsondata}')
        if jsondata['data']:
            # 已获取到数据
            return secid_, jsondata

    # 如果所有可能的secid全部尝试，记录股票代码信息
    print(f'{code} 尝试所有前缀({SECIDS})失败：')

    return None, None


def get_newestprice(codes):
    """
    获取股票最新价格 (带最新价格日期)

    :param codes: 股票代码列表
    :return:
        【dict】 - {'300750': [日期, 最新价格], ......}
        【Time out】 - 超时
        【Network error】 - 网络错误
    """
    rslt = {}  # 存储成功获取最新价格的数据

    url = 'https://push2.eastmoney.com/api/qt/stock/trends2/get?'
    params = {
        'fields1': 'f1',  # 可选值f1 - fn。这里只用到 f2(),f12
        'fields2': 'f51,f53',  # f51时间；f52收盘价
    }
    # 获取本地secids
    secids = read_secids()
    for code in codes:
        print(f'正在获取 {code} 的最新价格...')

        data = None  # 爬取到的数据
        # 判断本地是否存储了secid
        try:
            secid_ = secids[str(code)]
            secid = secid_ + str(code)

            # 访问最新价格接口
            params['secid'] = secid  # 股票的secid
            response = requests_get(url, params=params)
            if isinstance(response, str):
                # 如果返回的是str，说明访问有误
                return response
            # 无访问错误
            jsondata = response.json()
            # 判断返回是否有数据
            if jsondata['data']:
                data = jsondata['data']
        except KeyError:
            print(f'本地未存储 {code} 的 secid 前缀')
            pass

        # 如果data为空, 尝试所有可能的secid
        if not data:
            secid, jsondata = try_get(code, url, params=params)
            if not secid:
                # 所有可能的secid尝试失败，可能股票代码错误，或其他
                tryallsecidlog(f'get_newestprice({code})\n')
                print(f'跳过 {code}')
                continue
            else:
                # 存储或改变secid
                secids[str(code)] = secid
                data = jsondata['data']

        # 处理数据
        newestPrice, newestDate = data['trends'][-1].split(',')
        print(f'{code} 最新价格：{newestPrice,newestDate}')
        rslt[str(code)] = newestPrice, newestDate

    # 存储成功的secid
    write_secids(secids)
    return rslt


def get_stockinfo(code):
    """
    获取个股信息

    :param code: 股票代码
    :return:
        【dict】 - {
            "f2": 最新价
            "f3": 浮率
            "f4": 浮动价格
            "f5": 成交量（手）
            "f6": 成交额（元）
            "f8": 换手率
            "f9": 动态市盈率
            "f10": 量比
            "f12": 股票代码
            "f14": 股票简称
            "f15": 最高
            "f16": 最底
            "f17":  今开
            "f18": 昨收
            "f20": 总市值（元）
            "f21": 流通市值（元）
            "f23": 市净
        }
        【'TryAll error'】 - 所有secid尝试失败
        【'Time out'】 - 超时
        【'Network error'】 - 网络错误
    """
    url = 'https://push2.eastmoney.com/api/qt/ulist.np/get?'
    fields_par = [
        "f2",  # 最新价
        "f3",  # 浮率
        "f4",  # 浮动价格
        "f5",  # 成交量（手）
        "f6",  # 成交额（元）
        "f8",  # 换手率
        "f9",  # 动态市盈率
        "f10",  # 量比
        "f12",  # 股票代码
        "f14",  # 股票简称
        "f15",  # 最高
        "f16",  # 最底
        "f17",  # 今开
        "f18",  # 昨收
        "f20",  # 总市值（元）
        "f21",  # 流通市值（元）
        "f23"  # 市净
    ]
    fields = ''
    for par in fields_par:
        fields += par + ','
    fields = fields[:-1]
    params = {
        'fltt': 2,  # 2位小数
        'invt': 2,  # Unknow
        'fields': fields  # 可选值f1 - fn。暂只了解到 f30
    }
    # 获取本地secids
    secids = read_secids()
    data = None  # 爬取到的数据
    # 判断本地是否存储了secid
    try:
        secid_ = secids[str(code)]
        secid = secid_ + str(code)

        # 访问接口
        params['secids'] = secid  # 股票的secid
        response = requests_get(url, params=params)
        if isinstance(response, str):
            # 如果返回的是str，说明访问有误
            return response
        # 无访问错误
        jsondata = response.json()
        # 判断返回是否有数据
        if jsondata['data']:
            data = jsondata['data']
    except KeyError:
        print(f'本地未存储 {code} 的 secid 前缀')
        pass

    # 如果data为空, 尝试所有可能的secid
    if not data:
        tryReponse = try_get(code, url, sectype='secids', params=params)
        if isinstance(tryReponse, str):
            # 如果返回的是字符串，访问网址出错
            return tryReponse
        secid, jsondata = tryReponse
        if not secid:
            # 所有可能的secid尝试失败，可能股票代码错误，或其他
            tryallsecidlog(f'get_stockinfo({code})\n')
            return 'TryAll error'
        else:
            # 存储或改变secid
            secids[str(code)] = secid
            data = jsondata['data']

    # 存储成功的secid
    write_secids(secids)
    return data['diff'][0]


def download_stocktrend(code):
    """
    下载个股走势图

    :param code: 股票代码
    :return:
        【True】 - 下载完成
        【Flase】 - 下载失败
    """
    url = 'https://webquoteklinepic.eastmoney.com/GetPic.aspx?'
    params = {
        'imageType': 'KXL'  # 固定
    }
    # 获取本地secids
    secids = read_secids()
    piccode = None  # 爬取到的图片编码
    # 判断本地是否存储了secid
    try:
        secid_ = secids[str(code)]
        secid = secid_ + str(code)

        # 访问接口
        params['nid'] = secid  # 股票的secid
        response = requests_get(url, params=params)
        if isinstance(response, str):
            # 如果返回的是str，说明访问有误
            return False
        # 无访问错误
        piccode = response.content  # 图片的编码

    except KeyError:
        print(f'本地未存储 {code} 的 secid 前缀')
        pass

    # 对比图片是否为暂无数据，如果是。可能secid错误
    noneDatePIC = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02\x08\x00\x00\x01m\x08\x03\x00\x00\x00U\xf3b%\x00\x00\x03\x00PLTE\x00\x00\x00\xff\xff\xff\x80\x80\x80\x03\x03\x03\x04\x04\x04\x05\x05\x05\x06\x06\x06\x07\x07\x07\x08\x08\x08\t\t\t\n\n\n\x0b\x0b\x0b\x0c\x0c\x0c\r\r\r\x0e\x0e\x0e\x0f\x0f\x0f\x10\x10\x10\x11\x11\x11\x12\x12\x12\x13\x13\x13\x14\x14\x14\x15\x15\x15\x16\x16\x16\x17\x17\x17\x18\x18\x18\x19\x19\x19\x1a\x1a\x1a\x1b\x1b\x1b\x1c\x1c\x1c\x1d\x1d\x1d\x1e\x1e\x1e\x1f\x1f\x1f   !!!"""###$$$%%%&&&\'\'\'((()))***+++,,,---...///000111222333444555666777888999:::;;;<<<===>>>???@@@AAABBBCCCDDDEEEFFFGGGHHHIIIJJJKKKLLLMMMNNNOOOPPPQQQRRRSSSTTTUUUVVVWWWXXXYYYZZZ[[[\\\\\\]]]^^^___```aaabbbcccdddeeefffggghhhiiijjjkkklllmmmnnnooopppqqqrrrssstttuuuvvvwwwxxxyyyzzz{{{|||}}}~~~\x7f\x7f\x7f\x80\x80\x80\x81\x81\x81\x82\x82\x82\x83\x83\x83\x84\x84\x84\x85\x85\x85\x86\x86\x86\x87\x87\x87\x88\x88\x88\x89\x89\x89\x8a\x8a\x8a\x8b\x8b\x8b\x8c\x8c\x8c\x8d\x8d\x8d\x8e\x8e\x8e\x8f\x8f\x8f\x90\x90\x90\x91\x91\x91\x92\x92\x92\x93\x93\x93\x94\x94\x94\x95\x95\x95\x96\x96\x96\x97\x97\x97\x98\x98\x98\x99\x99\x99\x9a\x9a\x9a\x9b\x9b\x9b\x9c\x9c\x9c\x9d\x9d\x9d\x9e\x9e\x9e\x9f\x9f\x9f\xa0\xa0\xa0\xa1\xa1\xa1\xa2\xa2\xa2\xa3\xa3\xa3\xa4\xa4\xa4\xa5\xa5\xa5\xa6\xa6\xa6\xa7\xa7\xa7\xa8\xa8\xa8\xa9\xa9\xa9\xaa\xaa\xaa\xab\xab\xab\xac\xac\xac\xad\xad\xad\xae\xae\xae\xaf\xaf\xaf\xb0\xb0\xb0\xb1\xb1\xb1\xb2\xb2\xb2\xb3\xb3\xb3\xb4\xb4\xb4\xb5\xb5\xb5\xb6\xb6\xb6\xb7\xb7\xb7\xb8\xb8\xb8\xb9\xb9\xb9\xba\xba\xba\xbb\xbb\xbb\xbc\xbc\xbc\xbd\xbd\xbd\xbe\xbe\xbe\xbf\xbf\xbf\xc0\xc0\xc0\xc1\xc1\xc1\xc2\xc2\xc2\xc3\xc3\xc3\xc4\xc4\xc4\xc5\xc5\xc5\xc6\xc6\xc6\xc7\xc7\xc7\xc8\xc8\xc8\xc9\xc9\xc9\xca\xca\xca\xcb\xcb\xcb\xcc\xcc\xcc\xcd\xcd\xcd\xce\xce\xce\xcf\xcf\xcf\xd0\xd0\xd0\xd1\xd1\xd1\xd2\xd2\xd2\xd3\xd3\xd3\xd4\xd4\xd4\xd5\xd5\xd5\xd6\xd6\xd6\xd7\xd7\xd7\xd8\xd8\xd8\xd9\xd9\xd9\xda\xda\xda\xdb\xdb\xdb\xdc\xdc\xdc\xdd\xdd\xdd\xde\xde\xde\xdf\xdf\xdf\xe0\xe0\xe0\xe1\xe1\xe1\xe2\xe2\xe2\xe3\xe3\xe3\xe4\xe4\xe4\xe5\xe5\xe5\xe6\xe6\xe6\xe7\xe7\xe7\xe8\xe8\xe8\xe9\xe9\xe9\xea\xea\xea\xeb\xeb\xeb\xec\xec\xec\xed\xed\xed\xee\xee\xee\xef\xef\xef\xf0\xf0\xf0\xf1\xf1\xf1\xf2\xf2\xf2\xf3\xf3\xf3\xf4\xf4\xf4\xf5\xf5\xf5\xf6\xf6\xf6\xf7\xf7\xf7\xf8\xf8\xf8\xf9\xf9\xf9\xfa\xfa\xfa\xfb\xfb\xfb\xfc\xfc\xfc\xfd\xfd\xfd\xfe\xfe\xfe\xff\xff\xff"\x1fv\x0c\x00\x00\x00\tpHYs\x00\x00\x0e\xc3\x00\x00\x0e\xc3\x01\xc7o\xa8d\x00\x00\x050IDATx\x9c\xed\xdc\x81j\xe2Z\x14@\xd1\xf1\xfc\xffG\xbf\xd7\xdd\x18o\xc2@\xa5\x08m\x9c\xb5\x8a\xd6&6\x14\xdc9\xb9\xca0\x7fn\xf0\xbf??\xfd\x07\xf0;\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81|7\x84?\\\xdd\x8bB\xf8\xe6\xef\xf1[\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\xe1l>\xef\xe7\xaf;f}\xce[\x11\xc2\xd1|\xb8g0w\x1f\x8f\xb7\xaf\xcf\x1d\xf7}?\xf7\x87\xbe\x9a\x10Nz\xb1g\xfdy}\xb4\x84p\xdcyyB8\xd9&\xc2~\xb6\xcfc\xf3\xb6cy\x8a\x10\xbe8\xcc\xa5\xcdm\x1d\n\xfbk=s\x98\x04\xf3V\xf3@\x08g\xdb\x89\xbf,\x03\xb63\x7f\xb6\x12\xf6\x10L\x84\'\x0esis\\\rl\xeb\xc6\xc3rq\xce+\x89\xeb\x13\xc2\xd9\x1c\xce\xf59^#f\x1b\x06&\xc2s\x87\xb9\xb2Y\xee\x1f\xe7\xfd\xe3\x02q\xdf\xbc\x8c\x8dw \x84\xb3u"\xcc\xe36\xeb\xfb\xc79^?\xde\x81\x10\xce\x96\x89\xb0\xdfu\xbb\x7f\xac\xb4}six\xe20W\xb6~\x98\xb8oX\xde2\xec\x8b\x84\x9b\x89\xf0\xe5a\xb8\x1c!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x10!\x90W\x85\xc0\xd5\xbd&\x04\xde\x8c\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\x10\x88\xffU\xed\x9f\xf5\xa2\x10\xbe\xf9{\xfc\x16B B B B B B B B B B B B B B B B B B8\x9b\xcf\xfb\xf9\xeb\x8eY\x9f\xf3V\x84p4\x1f\xee\x19\xcc\xdd\xc7\xe3\xed\xebs\xc7}\xdf\xcf\xfd\xa1\xaf&\x84\x93^\xecY\x7f^\x1f-!\x1cw^\x9e\x10N\xb6\x89\xb0\x9f\xed\xf3\xd8\xbc\xedX\x9e"\x84/\x0esis[\x87\xc2\xfeZ\xcf\x1c&\xc1\xbc\xd5<\x10\xc2\xd9v\xe2/\xcb\x80\xed\xcc\x9f\xad\x84=\x04\x13\xe1\x89\xc3\\\xda\x1cW\x03\xdb\xba\xf1\xb0\\\x9c\xf3J\xe2\xfa\x84p6\x87s}\x8e\xd7\x88\xd9\x86\x81\x89\xf0\xdca\xael\x96\xfb\xc7y\xff\xb8@\xdc7/c\xe3\x1d\x08\xe1l\x9d\x08\xf3\xb8\xcd\xfa\xfeq\x8e\xd7\x8fw \x84\xb3e"\xecw\xdd\xee\x1f+m\xdf\\\x1a\x9e8\xcc\x95\xad\x1f&\xee\x1b\x96\xb7\x0c\xfb"\xe1f"|y\x18.G\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08\xe4U!pu\xaf\t\x817#\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04"\x04\xe2_1\xff\xb3^\x14\xc27\x7f\x8f\xdfB\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08D\x08g\xf3y?\x7f\xdd1\xebs\xde\x8a\x10\x8e\xe6\xc3=\x83\xb9\xfbx\xbc}}\xee\xb8\xef\xfb\xb9?\xf4\xd5\x84p\xd2\x8b=\xeb\xcf\xeb\xa3%\x84\xe3\xce\xcb\x13\xc2\xc96\x11\xf6\xb3}\x1e\x9b\xb7\x1d\xcbS\x84\xf0\xc5a.mn\xebP\xd8_\xeb\x99\xc3$\x98\xb7\x9a\x07B8\xdbN\xfce\x19\xb0\x9d\xf9\xb3\x95\xb0\x87`"<q\x98K\x9b\xe3j`[7\x1e\x96\x8bs^I\\\x9f\x10\xce\xe6p\xae\xcf\xf1\x1a1\xdb00\x11\x9e;\xcc\x95\xcdr\xff8\xef\x1f\x17\x88\xfb\xe6el\xbc\x03!\x9c\xad\x13a\x1e\xb7Y\xdf?\xce\xf1\xfa\xf1\x0e\x84p\xb6L\x84\xfd\xae\xdb\xfdc\xa5\xed\x9bK\xc3\x13\x87\xb9\xb2\xf5\xc3\xc4}\xc3\xf2\x96a_$\xdcL\x84/\x0f\xc3\xe5\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\x08\x81\xbc*\x04\xae\xee\xf8\x82\xfe\x07\x92\x00\xdbQIf\x9a\x8b\x00\x00\x00\x00IEND\xaeB`\x82'
    if not piccode or piccode == noneDatePIC:
        # 尝试所有secid
        tryall = True  # 如果tryall == True说明所有secid尝试失败
        for tsecid_ in SECIDS:
            print(f'下载图片 — 尝试{tsecid_}前缀')
            params['nid'] = tsecid_ + str(code)  # 股票的secid
            response = requests_get(url, params=params)
            if isinstance(response, str):
                # 如果返回的是str，说明访问有误
                return False
            # 无访问错误
            piccode = response.content  # 图片的编码
            if piccode != noneDatePIC:
                tryall = False
                print(f'下载图片成功！')
                # 存储或改变secid前缀
                secids[str(code)] = tsecid_
                break
        if tryall:
            # 所有secid尝试失败
            tryallsecidlog(f'download_stocktrend({code})\n')
            return False

    # 图片下载成功
    with open(FILE_TEMPPIC, 'wb+') as temp_pic:
        temp_pic.write(piccode)

    write_secids(secids)
    return True


def get_jjrank(ft="all", sc="6yzf", pn="50"):
    """
    获取基金排名

    :param ft: 类型（全部:all 股票：gp 混合：hh 指数：zs）
    :param sc: 排行时间段（近一周：zzf 近1月：1yzf 近3月：3yzf 近6月：6yzf 近1年：1nzf 近3年：3nzf）
    :param pn: 显示排行数
    :return:
        【'Time out'】 - 请求超时
        【'Network error'】 - 网络连接错误
        【'Response error'】 - 响应内容可能变更
        【list】 - [
            [
                0_"基金代码", 1_"基金简称", 2_"首字母", 3_"最新日期", 4_"最新净值", 5_"累计净值", 6_"日增长率", 7_"近1周收益率",
                8_"近1月收益率", 9_"近3月收益率", 10_"近6月收益率", 11_"近1年收益率", 12_"近2年收益率", 13_"近3年收益率",
                14_"今年来收益率", 15_"成立以来收益率", 16_"成立日期", 17_"Unknown1", 18_"自定义收益率", 19_"Unknown2",
                20_"Unknown3", 21_"Unknown4", 22_"手续费", 23_"Unknown5"
            ], ......
        ]
    """
    # print(f'参数：{ft, sc, pn}')

    url = "http://fund.eastmoney.com/data/rankhandler.aspx?"
    headers = {
        'User-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36',
        'Connection': 'keep-alive',
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Host': 'fund.eastmoney.com',
        'Referer': 'http://fund.eastmoney.com/data/fundranking.html'
    }
    params = {
        "op": "ph",
        "dt": "kf",
        "ft": ft,
        "sc": sc,
        "st": "desc",
        "pn": pn,
        "dx": "1",
    }
    # 开始访问
    response = requests_get(url, headers, params)
    if isinstance(response, str):
        # 访问出错
        return response
    # 访问成功，处理返回数据
    data = response.text
    start = data.index('[')
    end = data.index(']')
    data = data[start:end + 1]
    try:
        # 将处理完的数据转换成列表
        data = eval(data)
    except:
        # 如果报错说明，返回的格式变更
        responselog(url, params, response.text)
        return 'Response error'
    # 处理为嵌套列表
    data_list = []
    for line in data:
        # print(line)
        line2 = line.split(",")
        # print(line2)
        data_list.append(line2)
    # 处理完成
    return data_list


def get_jjbyname(key):
    """
    按照基金名称是否包含关键字，获取基金列表

    :param key: 名称
    :return:
        【'Time out'】 - 请求超时
        【'Network error'】 - 网络连接错误
        【'Response error'】 - 响应内容可能变更
        【list】 - [
            {'_id': '110022', 'CODE': '110022', 'NAME': '易方达消费行业股票', 'STOCKMARKET': ''},
            ......
        ]
    """
    url = "http://fundsuggest.eastmoney.com/FundSearch/api/FundSearchPageAPI.ashx?"
    headers = {
        'User-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36',
        'Connection': 'keep-alive',
        'Accept': '*/*',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-CN,zh;q=0.9',
        'Host': 'fundsuggest.eastmoney.com',
        'Referer': 'http://fund.eastmoney.com/'
    }
    params = {
        "m": "1",
        "key": key,
        "pageindex": "0",
        "pagesize": "1000",
    }
    # 开始访问
    response = requests_get(url, headers, params)
    if isinstance(response, str):
        # 访问出错
        return response
    # 访问成功，处理返回数据
    data = response.text
    start = data.index('[')
    end = data.index(']')
    data = data[start:end + 1]
    try:
        # 将处理完的数据转换成列表
        data = eval(data)
    except:
        # 如果报错说明，返回的格式变更
        responselog(url, params, response.text)
        return 'Response error'

    return data


def get_jjheld(code: str, year):
    """
    获取基金一年的股票持仓

    :param code: str 基金代码
    :param year: 年份
    :return:
        【'Time out'】 - 请求超时
        【'Network error'】 - 网络连接错误
        【None】 - 无数据
        【dict】 - {
            '截止': '2021-06-30',
            '持仓': {
                '表头': ['链接', '序号', '股票代码', '股票名称', '最新价', '涨跌幅', '相关资讯', '占净值比例', '持股数（万股）', '持仓市值（万元）']
                '数据': [
                    [],......
                ]
            }
        }
    """
    url = "http://fundf10.eastmoney.com/FundArchivesDatas.aspx?"
    params = {
        "type": "jjcc",
        "code": code,
        "topline": "10",
        "year": year,
    }
    # 开始访问
    response = requests_get(url, params=params)
    if isinstance(response, str):
        # 访问出错
        return response
    # print(f'相应数据：{response.text}')
    # 访问成功开始分析数据
    hp = Histheld()  # html分析
    hp.feed(response.text)
    hp.close()
    # 返回最新数据
    if not hp.rslt:
        # 无持仓数据，可能基金未公布财报
        return None
    # 有数据
    return hp.rslt[0]  # 返回最新的持仓


# ===============
# 分析基金持仓数据
# ===============
class Histheld(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.rslt = []  # 存储结果
        self.adir = {}  # 一个季度数据
        self.hold = {
            "表头": ['链接', '序号', '股票代码', '股票名称', '最新价', '涨跌幅', '相关资讯', '占净值比例', '持股数（万股）', '持仓市值（万元）'],
            "数据": [],
        }  # 一个季度持仓

        self.h4 = False  # 获取截止日期标志位
        self.h4_font = False  # 截止日期。self.h4 and self.h4_font记录截止日期
        self.table = False  # 持仓数据获取标志位
        self.tbody = False  # 数据截取标志位
        self.td = False
        self.num = 0
        self.herf = False  # 截取链接

        self.adata = ""  # 存储一个单元格数据
        self.linedata = []  # 存储一行数据

    def handle_starttag(self, tag, attrs):
        if tag == "h4":
            self.h4 = True
        elif tag == "font":
            self.h4_font = True
        elif tag == "tbody":
            self.tbody = True
        elif tag == "a" and self.num == 1:  # 截取链接
            for ats, value in attrs:
                if ats == "href":
                    self.linedata.insert(0, value)

    def handle_endtag(self, tag):
        if tag == "h4":
            self.h4 = False
        elif tag == "font":
            self.h4_font = False
        elif tag == "tbody":
            self.adir["持仓"] = self.hold.copy()
            self.rslt.append(self.adir.copy())
            self.hold["数据"] = []
            self.tbody = False
        elif tag == "td":
            self.linedata.append(self.adata)
            self.adata = ""
            self.num += 1
        elif tag == "tr" and self.tbody:
            self.hold["数据"].append(self.linedata)
            self.linedata = []
            self.num = 0

    def handle_data(self, data):
        if self.h4 and self.h4_font:
            self.adir["截止"] = data
        elif self.tbody:
            self.adata += data


def get_stock_daytrend(code: str, beg='0', end='20500000'):
    """
    获取股票的哪个时间段的日K

    :param code: 股票代码
    :param beg: 20210101 开始时间，如果不传，表示从上市开始的日k
    :param end: 结束时间，不传默认最新
    :return:
        【'TryAll error'】 - 所有secid尝试失败
        【'Time out'】 - 超时
        【'Network error'】 - 网络错误
        【list】 - K线数据: ['日期,最高,最低', '2021-07-01,23.50,22.52', ......]
    """
    url = 'http://push2his.eastmoney.com/api/qt/stock/kline/get?'
    params = {
        'fields1': 'f1,f3',  # 股票信息：f1-股票代码，f3-股票名称
        'fields2': 'f51,f54,f55',  # f51-时间（2021-06-29 11:10），f54-最高，f55-最低
        'klt': 101,  # Unknow，必要的
        'fqt': 1,  # Unknow，必要的
        'beg': beg,
        'end': end,
    }
    # 获取本地secids
    secids = read_secids()
    data = None  # 爬取到的数据
    # 判断本地是否存储了secid
    try:
        secid_ = secids[str(code)]
        secid = secid_ + str(code)

        # 访问接口
        params['secid'] = secid  # 股票的secid
        response = requests_get(url, params=params)
        if isinstance(response, str):
            # 如果返回的是str，说明访问有误
            return response
        # 无访问错误
        jsondata = response.json()
        # 判断返回是否有数据
        if jsondata['data']:
            data = jsondata['data']
    except KeyError:
        print(f'本地未存储 {code} 的 secid 前缀')
        pass

    # 如果data为空, 尝试所有可能的secid
    if not data:
        tryReponse = try_get(code, url, params=params)
        if isinstance(tryReponse, str):
            # 如果返回的是字符串，访问网址出错
            return tryReponse
        secid, jsondata = tryReponse
        if not secid:
            # 所有可能的secid尝试失败，可能股票代码错误，或其他
            tryallsecidlog(f'get_stockinfo({code})\n')
            return 'TryAll error'
        else:
            # 存储或改变secid
            secids[str(code)] = secid
            data = jsondata['data']

    # 存储成功的secid
    write_secids(secids)
    return data['klines']  # 返回K线数据
